package com.yayo.mangement.utils;

import com.yayo.mangement.authentication.annotations.ExcelProperty;
import com.yayo.mangement.authentication.enums.SuffixEnum;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.CollectionUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class ExcelUtils {

    private XSSFWorkbook workbook;
    private XSSFSheet sheet;
    private String title;
    private int columnSize = 0;
    private AtomicInteger nextRowIndex = new AtomicInteger(0);
    private int mergeRowSize = 0;

    public static XSSFWorkbook exportExcel(List<Object> exportList, Class<?> clazz) {
        ExcelUtils excelUtils = new ExcelUtils();
        return excelUtils.initData(exportList, clazz);
    }

    public XSSFWorkbook initData(List<Object> exportList, Class<?> clazz) {
        createWorkbook();
        createSheet();
        caculateColumnSize(clazz);
        createTitle(clazz);
        createColumnTitle(clazz);
        try {
            buildContent(exportList, clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return workbook;
    }

    private void createWorkbook() {
        workbook = new XSSFWorkbook();
    }

    private void createSheet() {
        sheet = workbook.createSheet();
    }

    private void createSheet(String sheetName) {
        sheet = workbook.createSheet(sheetName);
    }

    private void caculateColumnSize(Class<?> clazz) {
        caculateColumnSize(clazz, 0);
    }

    private void caculateColumnSize(Class<?> clazz, int currentDepth) {
        Field[] fieldArr = clazz.getDeclaredFields();
        boolean mergeRowDefault = true;
        // 更新最大深度
        if (currentDepth > mergeRowSize) {
            mergeRowSize = currentDepth;
        }
        for (Field field : fieldArr) {
            // 判断是否有ExcelProperty注解，如果没有则跳过不处理
            if (field.isAnnotationPresent(ExcelProperty.class)) {
                mergeRowDefault = false;
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                boolean isMergeRow = excelProperty.mergeRow();
                if(isMergeRow){
                    ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                    //判断集合的泛型里是否也有ExcelProperty注解
                    Class<?> innerClass = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                    caculateColumnSize(innerClass, currentDepth+1);
                }else{
                    columnSize ++;
                }
            }
        }
        if(mergeRowDefault){
            columnSize ++;
        }
    }

    private void createColumnTitle(Class<?> clazz) {
        int column = 0;
        XSSFRow row = sheet.createRow(nextRowIndex.get());
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(ExcelProperty.class)) {
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                String title = excelProperty.value().isEmpty() ? field.getName() : excelProperty.value();
                if (Collection.class.isAssignableFrom(field.getType()) && excelProperty.mergeRow()) {
                    Class<?> elementType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
                    Field[] innerFields = elementType.getDeclaredFields();
                    List<Field> annotatedInnerFields = Arrays.stream(innerFields)
                            .filter(f -> f.isAnnotationPresent(ExcelProperty.class))
                            .collect(Collectors.toList());
                    if (!annotatedInnerFields.isEmpty()) {
                        column = createMergeTitle(field, row, column, annotatedInnerFields);
                    } else {
                        addSingleCell(row, column, title, mergeRowSize);
                        column++;
                    }
                } else {
                    addSingleCell(row, column, title, mergeRowSize);
                    column++;
                }
            }
        }
        nextRowIndex.addAndGet(mergeRowSize + 1);
    }

    private int createMergeTitle(Field field, XSSFRow row, int column, List<Field> innerFields) {
        ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
        String titleValue = annotation.value();
        int size = innerFields.size();
        CellRangeAddress cellRangeAddress = new CellRangeAddress(nextRowIndex.get(), nextRowIndex.get(), column, column + size - 1);
        sheet.addMergedRegion(cellRangeAddress);
        XSSFCell mergeCell = row.createCell(column);
        mergeCell.setCellValue(titleValue);
        for (Field innerField : innerFields) {
            String innerTitle = innerField.getAnnotation(ExcelProperty.class).value().isEmpty() ? innerField.getName() : innerField.getAnnotation(ExcelProperty.class).value();
            XSSFRow nextRow = sheet.getRow(nextRowIndex.get() + 1);
            if (nextRow == null) {
                nextRow = sheet.createRow(nextRowIndex.get() + 1);
            }
            XSSFCell cell = nextRow.createCell(column++);
            cell.setCellValue(innerTitle);
        }
        return column;
    }

    private void addSingleCell(XSSFRow row, int column, String value, int mergeRows) {
        if (mergeRows > 0) {
            CellRangeAddress cellRangeAddress = new CellRangeAddress(nextRowIndex.get(), nextRowIndex.get() + mergeRows, column, column);
            sheet.addMergedRegion(cellRangeAddress);
        }
        XSSFCell cell = row.createCell(column);
        cell.setCellValue(value);
    }

    private void createTitle(Class<?> clazz) {
        ExcelProperty excelProperty = clazz.getAnnotation(ExcelProperty.class);
        if (excelProperty != null) {
            title = excelProperty.value();
            if (title != null) {
                XSSFRow titleRow = sheet.createRow(nextRowIndex.getAndIncrement());
                CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, 0, columnSize - 1);
                sheet.addMergedRegion(cellRangeAddress);
                XSSFCell cell = titleRow.createCell(0);
                cell.setCellValue(title);
            }
        }
    }

    public XSSFWorkbook buildContent(List<Object> exportList, Class<?> clazz) throws Exception {
        exportList.parallelStream().forEach(obj -> {
            try {
                processRow(obj, clazz, clazz.getDeclaredFields());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return workbook;
    }


    /**
     * 处理一行数据
     * @param obj 数据对象
     * @param clazz 数据对象类型
     * @param fieldArr
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private void processRow(Object obj, Class<?> clazz, Field[] fieldArr) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        int columnIndex = 0;
        int maxSize = 0;
        XSSFRow row = sheet.createRow(nextRowIndex.get());

        // 计算要合并的行数
        for (Field field : fieldArr) {
            if (field.isAnnotationPresent(ExcelProperty.class)) {
                ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
                if (annotation.mergeRow()) {
                    PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                    Method getMethod = pd.getReadMethod();
                    int size = getMethod.invoke(obj) instanceof List ? ((List<?>) getMethod.invoke(obj)).size() : 0;
                    if (size > maxSize) {
                        maxSize = size;
                    }
                }
            }
        }

        // 遍历获取的实体类列数组
        for (Field field : fieldArr) {
            if (field.isAnnotationPresent(ExcelProperty.class)) {
                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                Method getMethod = pd.getReadMethod();
                ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
                boolean isMergeRow = annotation.mergeRow();

                if (maxSize > 1 && !isMergeRow) {
                    CellRangeAddress cellRangeAddress = new CellRangeAddress(nextRowIndex.get(), nextRowIndex.get() + maxSize - 1, columnIndex, columnIndex);
                    sheet.addMergedRegion(cellRangeAddress);
                }

                if (isMergeRow) {
                    List<Object> list = (List<Object>) getMethod.invoke(obj);
                    if (!CollectionUtils.isEmpty(list)) {
                        Class<?> elementType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
                        List<Field> annotatedInnerFields = Arrays.stream(elementType.getDeclaredFields())
                                .filter(f -> f.isAnnotationPresent(ExcelProperty.class))
                                .collect(Collectors.toList());

                        if (!annotatedInnerFields.isEmpty()) {
                            for (int k = 0; k < list.size(); k++) {
                                Object element = list.get(k);
                                XSSFRow nextRow = sheet.getRow(nextRowIndex.get() + k);
                                if (nextRow == null) {
                                    nextRow = sheet.createRow(nextRowIndex.get() + k);
                                }
                                for (int j = 0; j < annotatedInnerFields.size(); j++) {
                                    Field childField = annotatedInnerFields.get(j);
                                    childField.setAccessible(true);
                                    Object fieldValue = childField.get(element);
                                    XSSFCell cell = nextRow.createCell(columnIndex + j);
                                    cell.setCellValue(fieldValue != null ? fieldValue.toString() : "");
                                }
                            }
                            columnIndex += (annotatedInnerFields.size() - 1);
                        } else {
                            for (int k = 0; k < list.size(); k++) {
                                XSSFRow listRow = sheet.getRow(nextRowIndex.get() + k);
                                if (listRow == null) {
                                    listRow = sheet.createRow(nextRowIndex.get() + k);
                                }
                                XSSFCell cell = listRow.createCell(columnIndex);
                                cell.setCellValue(list.get(k) + "");
                            }
                        }
                    }
                } else {
                    String columnValue = getMethod.invoke(obj) == null ? "" : getMethod.invoke(obj).toString();
                    XSSFCell cell = row.createCell(columnIndex);
                    cell.setCellValue(columnValue);
                }
                columnIndex++;
            }
        }
        nextRowIndex.addAndGet(maxSize);
    }

    /**
     * 生成时间戳文件名
     *
     * @return
     */
    private String getFileName(SuffixEnum suffixEnum) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String datetimeName = title + "_" + sdf.format(new Date());
        return datetimeName + "." + suffixEnum.getCode();
    }
}
